Slackで「今の電車の運行情報」を自分だけに教えてくれるSlash commandsを作った
はじめに
サーバーレス開発部の藤井元貴です。
以前、電車の運行情報を毎朝Slackに通知する仕組みを作りました。
このときは、毎朝8時に通知していましたが、
- 昼から外出するタイミングの運行情報は?
- 帰宅時の運行情報は?
- 休日の運行情報は?
なども知りたくなりました。人間とは欲深いものです。
そこで今回は、「今の運行情報」を自分だけに教えてくれるSlash Commandsを作成しました!!
おすすめの方
- 今の電車の遅延情報を知りたい
- AWS SAMで複数のLambda関数を定義したい
- Slackでアプリを作りたい
- SlackでSlach commandsを作りたい
- サーバーレスに興味がある
電車の運行情報
電車の運行情報については、下記のサイトを使用させていただきました。 使用にあたっては、下記サイトの「お約束」をご確認ください。
全体概要
前回の内容も含んでいます。
SlackのSlash comamnds
は、特定のURLに対して、「HTTP(POST)」でペイロードを送信します。
そのため、この情報を受け取るWebAPIを作成しています。
2つのLambda関数を作成しています。デプロイにAWS SAMを使用します。
Lambda関数 | 運行情報を見れる人 | やること |
---|---|---|
定期通知用 | チャンネルの全員 | 毎日、指定時刻に起動し、その時点の運行情報をSlackにPOSTする |
Slash commands用 | お願いした個人のみ | 好きなタイミングで個人が起動し、その時点の運行情報を個人向けに返答する |
- 定期通知用のLambda
- Slackの
incoming webhook
に対してメッセージをPOSTします - Slash commands用のLmabda
- Lambdaの戻り値(=API Gatewayの応答)でメッセージを返却します
- この処理は
3000ms以内
に実行する必要があります
環境
項目 | バージョン |
---|---|
macOS | High Sierra 10.13.6 |
AWS CLI | aws-cli/1.16.89 Python/3.6.1 Darwin/17.7.0 botocore/1.12.79 |
AWS SAM CLI | 0.10.0 |
Python | 3.6 |
AWS側の作成
Lambda関数
2つのLambda関数で共通利用するため、下記の処理を切り出しました。
- 通知対象の路線情報
- 共通の処理
Lambda関数は2つデプロイしますが、2つとも同じフォルダ(ファイル)をデプロイし、実行されるhandlerを変えています。 (他の方法としては、イイカンジにフォルダ構成を考える、Lambda Layerを使う、が考えられます。)
ファイル構成
主要なファイルの構成は下記となります。
├── hello_world │ ├── common_lambda.py │ ├── periodic.py │ ├── requirements.txt │ ├── slash_command.py │ └── target.json └── template.yaml
通知対象の路線ファイル(JSON)
この内容は任意にカスタマイズしてください!
[ { "name": "中央・総武各駅停車", "company": "JR東日本", "website": "https://traininfo.jreast.co.jp/train_info/kanto.aspx" }, { "name": "東西線", "company": "東京メトロ", "website": "https://www.tokyometro.jp/unkou/history/touzai.html" } ]
通知対象の路線を変更する場合は、このJSONファイルを修正すればOKです。
定期通知用
import os import json import requests from common_lambda import get_notify_delays, get_message SLACK_WEBHOOK_URL = os.environ['SLACK_WEBHOOK_URL'] def lambda_handler(event, context) -> None: notify_delays = get_notify_delays() if not notify_delays: # 遅延が無ければ通知しない return # Slack用のメッセージを作成して投げる (title, detail) = get_message(notify_delays) post_slack(title, detail) return def post_slack(title, detail) -> None: """SlackにPostする Args: title: メッセージのタイトル detail: メッセージの詳細(遅延情報) Returns: """ # https://api.slack.com/incoming-webhooks # https://api.slack.com/docs/message-formatting # https://api.slack.com/docs/messages/builder payload = { 'attachments': [ { 'color': '#36a64f', 'pretext': title, 'text': detail } ] } # http://requests-docs-ja.readthedocs.io/en/latest/user/quickstart/ try: response = requests.post(SLACK_WEBHOOK_URL, data=json.dumps(payload)) except requests.exceptions.RequestException as e: print(e) else: print(response.status_code)
Slash command用
import json from urllib.parse import unquote from common_lambda import get_notify_delays, get_message def lambda_handler(event, context) -> dict: # 受信したパラメータを解析する request_param = parse_slash_commands(event['body']) print(json.dumps(request_param)) if request_param['command'] != '/train': # 想定コマンドと異なるため何もしない return { "statusCode": 200, } notify_delays = get_notify_delays() # Slack用のメッセージを作成して返却する (title, detail) = get_message(notify_delays) # https://api.slack.com/incoming-webhooks # https://api.slack.com/docs/message-formatting # https://api.slack.com/docs/messages/builder # https://api.slack.com/slash-commands payload = { 'response_type': 'ephemeral', # コマンドを起動したユーザのみに返答する 'attachments': [ { 'color': '#36a64f', 'pretext': title, 'text': detail } ] } return { "statusCode": 200, "body": json.dumps(payload) } def parse_slash_commands(payload) -> dict: """Slash commandsのパラメータを解析する Args: payload: 受信したSlash commandsのパラメータ Returns: dict: 解析したパラメータとその内容 """ params = {} key_value_list = unquote(payload).split("&") for item in key_value_list: (key, value) = item.split("=") params[key] = value return params
共通処理用
import json import requests JSON_ADDR = 'https://rti-giken.jp/fhc/api/train_tetsudo/delay.json' def get_notify_delays() -> list: """通知すべき遅延情報を取得する Returns: list: 通知すべき遅延情報 """ current_delays = get_current_delays() target_list = get_target_list() notify_delays = [] for delay_item in current_delays: for check_item in target_list: if delay_item['name'] == check_item['name'] and delay_item['company'] == check_item['company']: notify_delays.append(check_item) return notify_delays def get_target_list() -> list: """判定対象の路線情報を取得する Returns: list: 判定対象の路線情報 """ with open('target.json') as f: return json.load(f) def get_current_delays() -> list: """ 現在の遅延情報を外部サイトから取得する Returns: list: 現在の遅延情報 """ try: res = requests.get(JSON_ADDR) except requests.RequestException as e: print(e) raise e if res.status_code == 200: return json.loads(res.text) return [] def get_message(delays) -> tuple: """Slackに通知するメッセージを作成する Args: delays: 通知すべき遅延情報 Returns: str: メッセージのタイトル str: メッセージの詳細(遅延情報) """ if not delays: return "電車の遅延はありません。", "" title = "電車の遅延があります。" details = [] for item in delays: company = item['company'] name = item['name'] website = item['website'] details.append(f'・{company}: {name}: <{website}|こちら>') return title, '\n'.join(details)
デプロイまで実行する
S3バケットの作成
コード等を格納するためのS3バケットを作成します。作成済みの場合は飛ばします。
aws s3 mb s3://cm-fujii.genki-deploy
ビルド
下記コマンドでビルドします。
sam build
package
続いてコード一式をS3バケットにアップロードします。
sam package \ --output-template-file packaged.yaml \ --s3-bucket cm-fujii.genki-deploy
deploy
最後にデプロイします。template.yaml
の環境変数をオーバーライドし、ここでSlackのWebhook URL
を設定します。
(詳細は前回を参照してください)
sam deploy \ --template-file packaged.yaml \ --stack-name NotifyTrainDelayToSlack \ --capabilities CAPABILITY_IAM \ --parameter-overrides SlackWebhookUrl=https://hooks.slack.com/services/xxxxxxxxxxxxx
WebAPIの確認
WebAPIのURLを確認します。
$ aws cloudformation describe-stacks --stack-name NotifyTrainDelayToSlack --query 'Stacks[].Outputs' [ [ { "OutputKey": "SlashCommandApi", "OutputValue": "https://xxxxxxxxxx.execute-api.ap-northeast-1.amazonaws.com/Prod/train/notification", "Description": "Slash command API" } ] ]
このURL(OutputValue
)は、SlackのSlash commandsの設定で使用します。
(Slash commandsがこのURLを叩くようにする)
Slackの準備
アプリの作成
Slackのアプリを作成します。
作成画面
任意のチャンネルを開き、「アプリを追加する」を選択します。
ブラウザが開くので、右上の「ビルド」を選択します。
左側にある「Building Slack apps」を選択します。
少し下側にある「Create a Slack app」を選択します。
もしくは、下記にアクセスすればOKです。
- <https://api.slack.com/apps>
新規作成
表示された画面の「Create New App」を選択します。
「App Name」を入力し、「Development Slack Workspace」を選択し、「Create App」を選択します。
これでアプリの作成まで完了しました。
アプリの設定
Slash Commandsの設定を行い、アプリをワークスペースにインストールします。
Slash Commands
「Slash Commands」を選択し、設定を行います。
「Create New Command」を選択します。
Commandに「/train」と入力し、Request URLに「さきほど確認したAPI GatewayのURL」を入力し、Short Descriptionに「簡単な説明」を入力します。
入力後は「Save」を選択します。
Basic Information
「Basic Information」を選択し、作成したアプリをワークスペースにインストールします。
「Install your app to your workspace」の「Install App to Workspace」を選択します。
「許可する」を選択します。
これで完了です!
使ってみる
Slackで/train
と入力します。
遅延があるとき
遅延がないとき
さいごに
EC2などのサーバーを立てなくても、簡単に実行できる環境が作れました。
サーバーレスは楽しいですね!!